﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

namespace Zhongy
{
    public class ServerPer
    {
        private Socket serverSocket;

        //private Semaphore acceptSemaphore;

        private ClientPerPool clientPerPool;
        private ClientPerPool curClientPerPool;

        private IApplication application;

        public void SetApplication(IApplication app)
        {
            this.application = app;
        }

        public void Start(int localport, int maxCount)
        {
            if (localport < 1 || localport > 65535)
                Debug.LogError("port is out of range");

            try
            {
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //acceptSemaphore = new Semaphore(maxCount, maxCount);

                clientPerPool = new ClientPerPool(maxCount);
                curClientPerPool = new ClientPerPool(maxCount);

                ClientPer tmpClientPeer = null;

                for (int i = 0; i < maxCount; i++)
                {
                    tmpClientPeer = new ClientPer();
                    tmpClientPeer.ReceiveArgs.Completed += receive_Completed;
                    tmpClientPeer.receiveCompleted = receiveCompleted;
                    tmpClientPeer.sendDisConnect = Disconnect;
                    clientPerPool.Enqueue(tmpClientPeer);
                }

                serverSocket.Bind(new IPEndPoint(IPAddress.Any, localport));
                serverSocket.Listen(10);

                Debug.Log("启动...");
                startAccept(null);
            }
            catch (Exception e)
            {
                Debug.LogError(e.Message);
            }
        }

        public void SendMsg(Action<ClientPer, object> sendAction, SocketSendMsg msg)
        {
            if (curClientPerPool.Count() > 0)
                curClientPerPool.DealAll(sendAction, msg);
        }

        public void CloseClients()
        {
            if (curClientPerPool.Count() > 0)
            {
                Debug.Log(string.Format("关闭使用中client"));
                //curClientPerPool.DealAll(Disconnect, "关闭使用中client");
                curClientPerPool.DealAll(Disconnect, "关闭使用中client");
            }

        }

        public void CloseSocket()
        {
            if (curClientPerPool.Count() > 0)
            {
                Debug.Log(string.Format("关闭使用中client"));
                curClientPerPool.DealAll(Disconnect, "关闭使用中client");
            }

            if (clientPerPool.Count() > 0)
            {
                Debug.Log(string.Format("关闭预备中client"));
                clientPerPool.DealAll(Disconnect, "关闭预备中client");
            }

            if (serverSocket == null)
                return;

            Debug.Log(serverSocket + "serverSocket 已关闭。");

            try
            {
                serverSocket.Shutdown(SocketShutdown.Both);
            }
            catch
            {
            }

            try
            {
                serverSocket.Close();
                serverSocket = null;
            }
            catch
            {
            }

        }


        private void startAccept(SocketAsyncEventArgs e)
        {
            if (e == null)
            {
                e = new SocketAsyncEventArgs();
                e.Completed += accept_Completed;
            }

            bool result = serverSocket.AcceptAsync(e);
            if (result == false)
            {
                processAccept(e);
            }
        }

        private void accept_Completed(object sender, SocketAsyncEventArgs e)
        {
            processAccept(e);
        }

        private void processAccept(SocketAsyncEventArgs e)
        {
            //acceptSemaphore.WaitOne();

            ClientPer client = clientPerPool.Dequeue();

            if(client==null)
                Debug.LogError("超出游戏允许接入的client的数目");

            curClientPerPool.Enqueue(client);

            if (client == null)
                Debug.LogError("?");

            client.ClientSocket = e.AcceptSocket;

            Debug.Log("客户端连接成功:" + client.ClientSocket.RemoteEndPoint.ToString());

            startRecive(client);

            e.AcceptSocket = null;
            //e = null;
            startAccept(e);
        }

        private void startRecive(ClientPer client)
        {
            try
            {
                bool result = client.ClientSocket.ReceiveAsync(client.ReceiveArgs);
                if (result == false)
                {
                    processReceive(client.ReceiveArgs);
                }
            }
            catch (Exception e)
            {
                Debug.LogError("startRecive:" + e.Message);
            }
        }

        private void receive_Completed(object sender, SocketAsyncEventArgs e)
        {
            processReceive(e);
        }

        private void processReceive(SocketAsyncEventArgs e)
        {
            ClientPer client = e.UserToken as ClientPer;
            if (client.ReceiveArgs.SocketError == SocketError.Success && client.ReceiveArgs.BytesTransferred > 0)
            {
                //Debug.Log("bytelength:"+ client.ReceiveArgs.BytesTransferred);

                byte[] packet = new byte[client.ReceiveArgs.BytesTransferred];
                Buffer.BlockCopy(client.ReceiveArgs.Buffer, 0, packet, 0, client.ReceiveArgs.BytesTransferred);

                client.StartReceive(packet);

                startRecive(client);

            }
            else if (client.ReceiveArgs.BytesTransferred == 0)
            {
                //todo 打开
                if (client.ReceiveArgs.SocketError == SocketError.Success)
                {

                    Debug.LogError("client.ReceiveArgs.BytesTransferred == 0 客户端主动断开连接");
                    //Disconnect(client, "客户端主动断开连接");
                }
                else
                {
                    //Disconnect(client, client.ReceiveArgs.SocketError.ToString());
                    Disconnect(client, "client.ReceiveArgs.BytesTransferred == 0"+client.ReceiveArgs.SocketError.ToString());
                }

            }
            else
            {
                Debug.LogError((client.ReceiveArgs.SocketError));
            }

        }

        private void receiveCompleted(ClientPer client, SocketReceiveMsg msg)
        {
#if UNITY_EDITOR
            Debug.LogError(DateTime.Now);
#endif

            application.OnReceive(client, msg);
        }


        public void Disconnect(ClientPer client, object reasonPacket) //= null)
        {
            try
            {
                string reason = string.Empty;
                if (reasonPacket != null)
                    reason = (string) reasonPacket;

                if (client == null)
                {
                    Debug.LogError("当前指定的client连接对象为空，无法断开连接");
                    return;
                }

                try
                {

                    Debug.Log(string.Format("{0}{1}{2}",
                        (client.ClientSocket != null) ? client.ClientSocket.RemoteEndPoint.ToString() : "备用",
                        "client断开连接原因：",
                        (string.IsNullOrEmpty(reason) ? string.Empty : reason
                        )));
                }
                catch (Exception e)
                {
                    Debug.Log(e.Message);
                }

                application.OnDisconnect(client);
                client.Disconnect();

                curClientPerPool.Dequeue(client);
                clientPerPool.Enqueue(client);
                //acceptSemaphore.Release();


            }
            catch (Exception e)
            {
                Debug.LogError(e.Message);
            }
        }
    }
}
